//------------------------------------------------------------------------------
// <copyright file="RpcChannelListener.h" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

#pragma once

#include <functional>
#include <vector>
#include <mutex>

#include "RpcSocketTransportMessage.h"
#include "IRpcChannel.h"
#include "IRpcStringSocket.h"
#include "ppltasks.h"

namespace Microsoft { namespace Internal { namespace GamesTest { namespace Rpc { namespace Server { namespace Ut
{
	class RpcChannelListenerTests;

}}}}}}

namespace Microsoft { namespace Internal { namespace GamesTest { namespace Rpc { namespace Server
{
	/// <summary>
	/// Implementations of this abstract class will listen for incoming socket connections and pass the 
	/// connected socket into this base class.  This class is responsible for maintaining the list of connected
	/// clients and passing their incoming data up the stack.
	/// </summary>
	class RpcChannelListener 
	{
	public: 
		typedef std::function<void (std::weak_ptr<IRpcChannel>, RpcSocketTransportMessage)> IncomingMessageCallback;
		typedef std::function<void (std::weak_ptr<IRpcChannel>)> ClientDisconnectedCallback;

	private:
		friend class Microsoft::Internal::GamesTest::Rpc::Server::Ut::RpcChannelListenerTests;

		typedef std::vector<std::shared_ptr<IRpcChannel>> ClientCollection;

		static RpcChannelListener* s_instance;

		ClientCollection m_clients;
		bool m_isStarted;
		IncomingMessageCallback m_incomingMessageCallback;
		ClientDisconnectedCallback m_clientDisconnectedCallback;
		std::mutex s_synchronizationContext;

	public:
		/// <summary>
		/// Start listening for connecting clients and incoming messages.
		/// </summary>
		GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT Start(int port, IncomingMessageCallback incomingMessageCallback, ClientDisconnectedCallback clientDisconnectedCallback);

		/// <summary>
		/// Stop listening for connecting clients and incoming messages.
		/// </summary>
		GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT Stop();

		/// <summary>
		/// Retrieves the pointer to the single instance of this class.
		/// </summary>
		static RpcChannelListener* Instance();

		/// <summary>
		/// Initializes a new instance of the RpcChannelListener class.
		/// </summary>
		RpcChannelListener();

	protected:

		virtual ~RpcChannelListener() {}

		/// <summary>
		/// Platform and API specific implementation of the logic required to start listening for connecting clients
		/// and their incoming messages.
		/// </summary>
		virtual GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT StartImpl(int port) = 0;

		/// <summary>
		/// Platform and API specific implementation of the logic required to stop listening for connecting clients
		/// and their incoming messages.
		/// </summary>
		virtual GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT StopImpl() = 0;

		/// <summary>
		/// Implementations of this class are expected to call this method any time that a client connects.
		/// </summary>
		void OnSocketConnected(IRpcStringSocket* socket);

		bool GetIsStarted()
		{
			return m_isStarted;
		}

		__declspec( property (get = GetIsStarted) ) bool IsStarted;

	private:
		// This function is useful because it can be mocked in the unit tests
		// to return custom implementations of the IRpcChannel interface.
		std::shared_ptr<IRpcChannel> CreateRpcChannel(IRpcStringSocket* socket);

		void OnChannelRequestReceived(std::weak_ptr<IRpcChannel> client, RpcSocketTransportMessage requestMessage);

		void OnChannelDisconnected(std::weak_ptr<IRpcChannel> client);

		RpcChannelListener(const RpcChannelListener& other);
	};

}}}}}